#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QMap>
#include <QMultiMap>
#include <QFileDialog>
#include <QTextStream>
#include <QDebug>
#include <QMessageBox>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    mAudioPlayer.setMedia(QUrl::fromLocalFile("/home/rigoligo/Desktop/bailan/audio.wav"));
    mAudioPlayer.setVolume(50);

    mSaved = false;

    connect(this, &MainWindow::ResourceDirChanged,
            ui->AnimArea, &AnimWidget::SetResourceDir);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::ParseAnim()
{
    auto animstr = ui->plainTextEdit->toPlainText();

    AddOpsFromCommands(animstr);
}

void MainWindow::AddOpsFromCommands(QString animstr)
{
    enum { Normal, InMacroTrack, InMacroEntity } status = Normal;
    using TMacroEntity = QString;
    QMap<QString, TMacroEntity> macroEntities;
    TMacroEntity *currMacroEntity = nullptr;
    auto lines = animstr.split('\n', QString::SplitBehavior::SkipEmptyParts);

    if(lines.size() && lines[0].indexOf("audio,") == 0)
    {
        mAudioPlayer.setMedia(QUrl::fromLocalFile(mResourceDir + lines[0].remove(0, 6)));
        lines.removeFirst();
    }
    else
    {
        mAudioPlayer.setMedia(QUrl());
    }

    for(auto &i : lines)
    {
        if(i[0] == '#') // Comment
            continue;

        auto l = ParseAnimLine(i, status == Normal);

        switch(status)
        {
        case InMacroEntity:
            if(l.Token == MacroEntity)
            {
                currMacroEntity = nullptr;
                if(l.Fields.isEmpty() || l.Fields[0].isEmpty()) status = Normal; // End macro block
            }
            else if(l.Token != Invalid)
            {
                currMacroEntity->append(i + '\n');
            }
            break;

        case InMacroTrack:
            if(l.Token == MacroTrack)
            {
                // TODO
            }
            break;

        case Normal:
            switch(l.Token)
            {
            case MacroEntity:
                if(l.Fields.isEmpty() || l.Fields[0].isEmpty()) // Macro block ending marker
                    continue;
                macroEntities[l.Fields[0]] = TMacroEntity(); // Otherwise start a new macro block
                currMacroEntity = &macroEntities[l.Fields[0]];
                status = InMacroEntity;
                break;

            case MacroTrack:
                // TODO
                break;

            case UseMacroEntity:
            {
                // Expand macro
                auto entityTmplte = macroEntities.find(l.Fields[0]);
                if(entityTmplte == macroEntities.end())
                    continue;
                auto newEntity = *entityTmplte;
                for(int k = 1; k < l.Fields.size(); k++)
                    newEntity = newEntity.arg(l.Fields[k]);

                // ADD to AnimOps, by recursivey calling self, this is only one level deep
                AddOpsFromCommands(newEntity);
                break;
            }

            default:
                auto op = CreateOpFromLine(l);
                if(op.Code != InvalidOp)
                    AnimOps.insert(op.At, op);
            }

            break;
        }
    }
}


void MainWindow::on_btnPlay_clicked()
{
    AnimOps.clear();
    ParseAnim();
    ui->AnimArea->Clear();
    ui->AnimArea->PrepareOpMap(AnimOps, ui->spnStartPos->value());
    ui->AnimArea->SetFrameTime(ui->spnFrameTime->value());
    int ms = ui->spnFrameTime->value() * 1000 + 0.5;
    mAnimTimer.start(ms,
                     Qt::TimerType::PreciseTimer,
                     ui->AnimArea);
    mAudioPlayer.setPosition(ui->spnStartPos->value() * 1000 + 0.5);
    mAudioPlayer.play();
}


void MainWindow::on_btnStop_clicked()
{
    mAnimTimer.stop();
    mAudioPlayer.stop();
}

void MainWindow::on_btnChooseAudio_clicked()
{
    mAudioPlayer.setMedia(
                QFileDialog::getOpenFileUrl(
                    nullptr,
                    "Select audio",
                    QUrl(),
                    "Audio (*.wav; *.mp3);;All files(*.*)")
                );
}

void MainWindow::on_btnLoad_clicked()
{
    QFile file;
    QString filename;
    forever
    {
        filename = QFileDialog::getOpenFileName(
                    nullptr,
                    "Load animation script",
                    "",
                    "Bailan Animation (*.blanim);;"
                    "Text file (*.txt)");
        if(filename.isNull())
            return;
        file.setFileName(filename);
        if(file.open(QFile::ReadOnly))
            break;
        QMessageBox::critical(this,
                              "Cannot open animation script",
                              "The file you've chosen is not readable. Please retry.");
    }
    filename = QDir::toNativeSeparators(filename);
    auto resourceDir = mCurrentFile.section(QDir::separator(), 0, -2),
            newRsrcDir = filename.section(QDir::separator(), 0, -2);
    if(resourceDir != newRsrcDir)
    {
        emit ResourceDirChanged((newRsrcDir += QDir::separator()));
        mResourceDir = newRsrcDir;
    }
    mCurrentFile = filename;
    setWindowTitle("Bailan Animator - " + filename.section(QDir::separator(), -1));
    QTextStream ts(&file);
    ui->plainTextEdit->setPlainText(ts.readAll());
    file.close();
}

void MainWindow::on_btnSave_clicked()
{
    QString filename;
    QFile file;
    if(mCurrentFile.isNull())
    {
        forever
        {
            filename = QFileDialog::getSaveFileName(
                        nullptr,
                        "Save animation script",
                        "",
                        "Bailan Animation (*.blanim);;"
                        "Text file (*.txt)");
            if(filename.isNull())
                return;
            file.setFileName(filename);
            if(file.open(QFile::WriteOnly))
            {
                setWindowTitle("Bailan Animator - " + filename.section(QDir::separator(), -1));
                mCurrentFile = filename;
                break;
            }
            QMessageBox::critical(this,
                                  "Cannot save animation script",
                                  "The file you've hosen is not writable. Please retry.");
        }
    }
    else
    {
        file.setFileName(mCurrentFile);
        if(!file.open(QFile::WriteOnly))
            QMessageBox::critical(this,
                                  "Cannot save animation script",
                                  "The file you've hosen is not writable. Please save manually.");
    }
    filename = QDir::toNativeSeparators(filename);
    auto resourceDir = mCurrentFile.section(QDir::separator(), 0, -2),
            newRsrcDir = filename.section(QDir::separator(), 0, -2);
    if(resourceDir != newRsrcDir)
    {
        emit ResourceDirChanged((newRsrcDir += QDir::separator()));
        mResourceDir = newRsrcDir;
    }
    QTextStream ts(&file);
    ts << ui->plainTextEdit->toPlainText();
    ts.flush();
    file.close();
    mSaved = true;
    ui->plainTextEdit->document()->setModified(false);
}

void MainWindow::on_plainTextEdit_modificationChanged(bool arg1)
{
    mSaved = arg1;
}
